#include "AlgorithmStats.hpp"
#include "GraphExploration.hpp"
#include <algorithm>
#include <numeric>

// include headers that implement an archive
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

// To serialize
#include <boost/serialization/vector.hpp>
#include <boost/serialization/set.hpp>
#include <boost/graph/adj_list_serialize.hpp>
#include <boost/serialization/utility.hpp> // For std::pair


TestingSet AlgorithmStats::GenerateTestingSet(vector<int> PGSizes)
{
    srand ( time(NULL) );

    TestingSet kRes;

    int iTotalPGs = std::accumulate( PGSizes.begin(), PGSizes.end(), 0 );
    kRes.m_PGs.resize(iTotalPGs);
    kRes.m_PGSizes = PGSizes;

    int iID = 0;

    // For each partial graph size
    for(int i=1; i < PGSizes.size(); i++)
    {
        // Generate PGSizes[i] partial graphs of size i
        for(int k=0;k < PGSizes[i]; k++)
        {
            // Pick a random graph that has atleast i vertices
            bool bGraphFound = false;
            int iGraphID; GraphHandler::Graph* pG = 0;
            while(pG == 0)
            {
                iGraphID = rand() % GraphHandler::Instance().GetNumGraphs();
                if( num_vertices(GraphHandler::Instance().GetGraph(iGraphID)) >= i )
                    pG = &GraphHandler::Instance().GetGraph(iGraphID);
            }

            // We've found a suitable graph, now select a random partial graph from it
            GraphExploration GE(*pG);

            GraphExploration::EditOpIterator it(GE);


            for(int j=1; j < i; j++)
            {
                // Add the other vertices in a random manner
                it.NextVertexOp(false);
                *it;
            }


            GraphHandler::Graph G = GE.GetPartialGraph();

            assert( num_vertices(G) == i );

            GraphHandler::Instance().SaveGraph("tmp.dot", G);

            kRes.m_PGs[iID] = make_pair(iGraphID, GE.GetPartialGraph());
            iID++;

        }

    }

    return kRes;
}
//----------------------------------------------------------------
void TestingSet::Save(string sFilename) const
{
    ofstream ofs(sFilename.c_str(), ios::binary);

    boost::archive::binary_oarchive oa(ofs);
    oa << (*this);
    ofs.close();
}
//----------------------------------------------------------------
void TestingSet::Load(string sFilename)
{
    std::ifstream ifs(sFilename.c_str(), ios::binary);


    boost::archive::binary_iarchive oa(ifs);
    oa >> (*this);

    ifs.close();
}
//----------------------------------------------------------------
double AlgorithmStats::TestAll(TestingSet& kT, bool bBForce, string sFilename)
{
    int iCorrect = 0;
    kT.iSuccIDs.clear();
    kT.iFailedIDs.clear();

    ofstream out(sFilename.c_str(), ios_base::out);

    out << kT.m_PGSizes.size() << endl;
    for(int i=0; i < kT.m_PGSizes.size(); i++)
        out << kT.m_PGSizes[i] << endl;

    int iCGraphSize = num_vertices(kT.m_PGs[0].second);
    int iCorrectTmp = 0;
    int iTmp = 0;
    int k = 0;

    /*
    for(int i=0; i < 14; i++)
        k += kT.m_PGSizes[i];*/

    int iM = 1;

    for(int i=0; i < kT.m_PGSizes.size(); i++)
    {
        iCorrectTmp = 0;

        for(int j=0; j < kT.m_PGSizes[i]; j++)
        {
    //for(int k=0; k < kT.m_PGs.size(); k++)
    //{



            GraphHandler::Instance().m_kPartialG = kT.m_PGs[k].second;
            GraphHandler::Instance().m_pEGraph = &GraphHandler::Instance().GetGraph(kT.m_PGs[k].first);
            //cout << "i: " << i << ", j:" << j << endl;

            GraphHandler::Instance().iExcludeGraph = kT.m_PGs[k].first;

            for(int z = 0; z < iM; z++)
            {
                //cout << "Calling predict" << endl;
                if(!bBForce)
                    //GraphHandler::Instance().m_kPartialG = GraphHandler::Instance().ExampleStepPredict2(&GraphHandler::Instance().m_kPartialG);
                    GraphHandler::Instance().m_kPartialG = GraphHandler::Instance().ExampleStepPredict2(&GraphHandler::Instance().m_kPartialG);
                else
                    GraphHandler::Instance().m_kPartialG = GraphHandler::Instance().BruteForcePredict(&GraphHandler::Instance().m_kPartialG);

                if( GraphHandler::Instance().m_bCorrectPrediction )
                {
                    kT.iSuccIDs.push_back(k);
                    iCorrect++;
                    iCorrectTmp++;
                }
                else
                    kT.iFailedIDs.push_back(k);
            }



            k++;
        }
        if( kT.m_PGSizes[i] != 0 )
        {
        // New graph size, save current
        out << (double)iCorrectTmp / (double)(kT.m_PGSizes[i] * iM) << endl;
        cout << (double)iCorrectTmp / (double)(kT.m_PGSizes[i] * iM) << endl;
        }

    }
    //}



    //for( set<string>::iterator it = kBlacklist.begin(); it != kBlacklist.end(); ++it )
    //    out << *it << endl;

    out.close();

    return (double)iCorrect / (double)(kT.m_PGs.size() * iM);
}

int AlgorithmStats::TestAllGED(TestingSet kT, bool bBForce)
{
    int iTotalDist = 0;
    int iM = 2;
    for(int i=0; i < kT.m_PGs.size(); i++)
    {
        GraphHandler::Instance().m_kPartialG = kT.m_PGs[i].second;
        GraphHandler::Instance().m_pEGraph = &GraphHandler::Instance().GetGraph(kT.m_PGs[i].first);
        for(int j=0; j < iM; j++)
        {
            if(!bBForce)
                GraphHandler::Instance().m_kPartialG = GraphHandler::Instance().ExampleStepPredict2(&GraphHandler::Instance().m_kPartialG);
            else
                GraphHandler::Instance().m_kPartialG = GraphHandler::Instance().BruteForcePredict(&GraphHandler::Instance().m_kPartialG);
        }
        iTotalDist += GraphHandler::Instance().EditDistance(GraphHandler::Instance().m_kPartialG, *GraphHandler::Instance().m_pEGraph);
    }

    return iTotalDist;
}
